package com.lee9213.core.config;


import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import javax.sql.DataSource;
import javax.transaction.TransactionManager;
import javax.transaction.UserTransaction;

import com.lee9213.core.config.properties.SlaveDataSourceProperties;
import com.lee9213.core.datasource.DynamicDataSource;
import com.lee9213.core.transaction.JtaTransactionManager;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.jta.atomikos.AtomikosDataSourceBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import com.alibaba.druid.pool.xa.DruidXADataSource;
import com.atomikos.icatch.jta.UserTransactionImp;
import com.atomikos.icatch.jta.UserTransactionManager;
import com.lee9213.core.config.properties.BaseDataSourceProperties;
import com.lee9213.core.config.properties.MasterDataSourceProperties;
import com.lee9213.core.util.AESUtil;

/**
 * 数据源配置
 *
 * @author libo
 * @version 1.0
 * @date 2017/11/10 15:03
 */
@Configuration
@EnableConfigurationProperties({ MasterDataSourceProperties.class, SlaveDataSourceProperties.class })
public class MyBatisConfiguration {

    @Autowired
    private MasterDataSourceProperties masterDataSourceProperties;

    @Autowired
    private SlaveDataSourceProperties slaveDataSourceProperties;

    @Bean(value = "master",initMethod = "init",destroyMethod = "close")
    @Primary
    public AtomikosDataSourceBean masterDataSource() throws SQLException {
        AtomikosDataSourceBean dataSourceBean = new AtomikosDataSourceBean();
        DruidXADataSource master = getDataSource(masterDataSourceProperties);
        dataSourceBean.setXaDataSource(master);
        dataSourceBean.setUniqueResourceName("master");
        dataSourceBean.setMaxPoolSize(masterDataSourceProperties.getMaxPoolSize());
        dataSourceBean.setReapTimeout(20000);
        return dataSourceBean;
    }

    @Bean(value = "slave1",initMethod = "init",destroyMethod = "close")
    public AtomikosDataSourceBean slaveDataSource1() throws SQLException {
        AtomikosDataSourceBean dataSourceBean = new AtomikosDataSourceBean();
        DruidXADataSource slave = getDataSource(slaveDataSourceProperties);
        dataSourceBean.setXaDataSource(slave);
        dataSourceBean.setUniqueResourceName("slave1");
        dataSourceBean.setMaxPoolSize(slaveDataSourceProperties.getMaxPoolSize());
        dataSourceBean.setReapTimeout(20000);
        return dataSourceBean;
    }

    @Bean(value = "slave2",initMethod = "init",destroyMethod = "close")
    public AtomikosDataSourceBean slaveDataSource2() throws SQLException {
        AtomikosDataSourceBean dataSourceBean = new AtomikosDataSourceBean();
        DruidXADataSource slave = getDataSource(slaveDataSourceProperties);
        dataSourceBean.setXaDataSource(slave);
        dataSourceBean.setUniqueResourceName("slave2");
        dataSourceBean.setMaxPoolSize(slaveDataSourceProperties.getMaxPoolSize());
        dataSourceBean.setReapTimeout(20000);
        return dataSourceBean;
    }

    /**
     * 动态数据源
     *
     * @return
     * @throws SQLException
     */
    @Bean
    public DynamicDataSource dataSource() throws SQLException {
        DynamicDataSource dataSource = new DynamicDataSource();
        dataSource.setMaster(masterDataSource());

        List<DataSource> slaves = new ArrayList();
        slaves.add(slaveDataSource1());
        slaves.add(slaveDataSource2());
        dataSource.setSlaves(slaves);

        dataSource.setDefaultTargetDataSource(masterDataSource());
        return dataSource;
    }

    /**
     * 根据数据源创建SqlSessionFactory
     */
    @Bean
    public SqlSessionFactory sqlSessionFactory(DynamicDataSource dynamicDataSource) throws Exception {
        SqlSessionFactoryBean fb = new SqlSessionFactoryBean();
        fb.setDataSource(dynamicDataSource);// 指定数据源(这个必须有，否则报错)
        // 下边两句仅仅用于*.xml文件，如果整个持久层操作不需要使用到xml文件的话（只用注解就可以搞定），则不加
        fb.setConfigLocation(new PathMatchingResourcePatternResolver().getResource("classpath:mybatis-config.xml"));
        fb.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mybatis/*Mapper.xml"));//

        return fb.getObject();
    }

    /**
     * 事务管理器
     *
     * @param
     * @return
     */
    @Bean(name = "userTransaction")
    public UserTransaction userTransaction() throws Throwable {
        UserTransactionImp userTransactionImp = new UserTransactionImp();
        userTransactionImp.setTransactionTimeout(10000);
        return userTransactionImp;
    }

    @Bean(name = "atomikosTransactionManager", initMethod = "init", destroyMethod = "close")
    public TransactionManager atomikosTransactionManager() throws Throwable {
        UserTransactionManager userTransactionManager = new UserTransactionManager();
        userTransactionManager.setForceShutdown(true);
        return userTransactionManager;
    }

    @Bean(name = "jtaTransactionManager")
    @DependsOn({ "userTransaction", "atomikosTransactionManager" })
    public JtaTransactionManager transactionManager() throws Throwable {
        JtaTransactionManager manager = new JtaTransactionManager();
        manager.setUserTransaction(userTransaction());
        manager.setTransactionManager(atomikosTransactionManager());
//        UserTransaction userTransaction = userTransaction();
//        JtaTransactionManager manager = new JtaTransactionManager(userTransaction, atomikosTransactionManager());
        return manager;
    }

    /**
     * 配置数据源
     *
     * @param dataSourceProperties 配置文件
     * @return
     * @throws SQLException
     */
    private final DruidXADataSource getDataSource(BaseDataSourceProperties dataSourceProperties) throws SQLException {
        DruidXADataSource dataSource = new DruidXADataSource();
        dataSource.setUrl(dataSourceProperties.getUrl());
        dataSource.setUsername(dataSourceProperties.getUsername());
        dataSource.setPassword(AESUtil.AESDecode(dataSourceProperties.getPassword()));
        dataSource.setDriverClassName(dataSourceProperties.getDriverClassName());
        dataSource.setInitialSize(dataSourceProperties.getInitialSize());
        dataSource.setMinIdle(dataSourceProperties.getMinIdle());
        dataSource.setMaxActive(dataSourceProperties.getMaxActive());
        dataSource.setMaxWait(dataSourceProperties.getMaxWait());
        dataSource.setTimeBetweenEvictionRunsMillis(dataSourceProperties.getTimeBetweenEvictionRunsMillis());
        dataSource.setMinEvictableIdleTimeMillis(dataSourceProperties.getMinEvictableIdleTimeMillis());
        dataSource.setValidationQuery(dataSourceProperties.getValidationQuery());
        dataSource.setTestWhileIdle(dataSourceProperties.isTestWhileIdle());
        dataSource.setTestOnBorrow(dataSourceProperties.isTestOnBorrow());
        dataSource.setTestOnReturn(dataSourceProperties.isTestOnReturn());
        dataSource.setFilters(dataSourceProperties.getFilters());
        dataSource.setRemoveAbandoned(dataSourceProperties.isRemoveAbandoned());
        dataSource.setRemoveAbandonedTimeout(dataSourceProperties.getRemoveAbandonedTimeout());
        dataSource.setConnectionProperties(dataSourceProperties.getConnectionProperties());
        return dataSource;
    }
}
